#include <iostream>
#include <chrono>
#include <fstream>
#include <vector>
#include <tuple>

// 实验目的: 通过实际程序的执行结果，了解程序访问的局部性对带有Cache 的计算机系统性能的影响。
// 实验要求: 在以下程序中，修改或添加必要的语句(如计时函数等)，以计算和打印主体程序段的执行时间。分别以M=1000，N=10；M=100，N=100；M=10，N=1000，执行程序A和程序B，以比较两个程序执行时间的长短。
// 程序段 A assign-array-rows ()
// {
// int i, j, a[M][N]; ...... 
// for (i= 0; i<M; i++)
//      for (j= 0; j<N; j++)
//          a[i][j]=i+j; ...... 
// } 

// 程序段 B assign-array-cols ()
// {
// int i, j, a[M][N]; ......
// for (j= 0; j<N; j++)
//      for (i=0; i<M; i++) 
//          a[i][j] = i + j;
// ...... 
// } 
// 说明局部数据块大小、数组访问顺序等和执行时间之间的关系。


std::fstream output("221900175output.out", std::ios::in | std::ios::out | std::ios::app);
const static int T = 100;

int64_t helper_assign_array_rows(int M, int N) {
    int i, j, a[M][N];
    // ...... other initialization code

    auto start_time = std::chrono::high_resolution_clock::now(); //记录开始时间

    for (i = 0; i < M; i++) {
        for (j = 0; j < N; j++) {
            output << (a[i][j] = i + j);
        }
    }

    output << std::endl;

    auto end_time = std::chrono::high_resolution_clock::now(); //记录结束时间

    int64_t execution_time = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count(); //计算执行时间

    return execution_time;
}

int64_t helper_assign_array_cols(int M, int N) {
    int i, j, a[M][N];
    // ...... other initialization code

    auto start_time = std::chrono::high_resolution_clock::now(); //记录开始时间

    for (j = 0; j < N; j++) {
        for (i = 0; i < M; i++) {
            output << (a[i][j] = i + j);
        }
    }

    output << std::endl;
    auto end_time = std::chrono::high_resolution_clock::now(); //记录结束时间

    int64_t execution_time = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count(); //计算执行时间

    return execution_time;
}

void assign_array_rows(int M, int N) {
    int64_t execution_time = 0ll;
    for (int t = 0; t < T; ++t) {
        execution_time += helper_assign_array_rows(M, N);
    }
    std::cout << "Execution time for assign_array_rows(" << M << ", " << N << "): " << execution_time / (double)T << " microseconds\n";
}

void assign_array_cols(int M, int N) {
    int64_t execution_time = 0ll;
    for (int t = 0; t < T; ++t) {
        execution_time += helper_assign_array_cols(M, N);
    }
    std::cout << "Execution time for assign_array_cols(" << M << ", " << N << "): " << execution_time / (double)T << " microseconds\n";
}

int main() {
    output.clear();
    std::vector<std::pair<int, int>>MN{
        {1000,10},{100,100},{10,1000}
    };
#if __cplusplus > 201402L
    // after c++17
    for (auto &[M, N] : MN) {
#else
    // before c++17
    for (auto &p : MN) {
        int M = p.first, N = p.second;
#endif
        assign_array_rows(M, N);
        assign_array_cols(M, N);
    }
    output.close();
    return 0;
}

// Execution time for assign_array_rows(1000, 10): 466.14 microseconds
// Execution time for assign_array_cols(1000, 10): 495.78 microseconds
// Execution time for assign_array_rows(100, 100): 439.96 microseconds
// Execution time for assign_array_cols(100, 100): 440.09 microseconds
// Execution time for assign_array_rows(10, 1000): 497.06 microseconds
// Execution time for assign_array_cols(10, 1000): 500.07 microseconds